package entity.gui;

import com.item.manager.serialize.*;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Vector;
import java.util.function.Function;

import static com.google.common.collect.Lists.newArrayList;
import static java.awt.event.ItemEvent.SELECTED;
import static java.lang.String.format;
import static java.util.Arrays.asList;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;

/**
 * Form for attribute editing.
 *
 * @author Blind
 */
public class AttributeEditorForm {

    private JComboBox<TemplateAdaptor> templateBox;

    private JTable attributeTable;

    private JPanel templateEditor;
    private JButton newAttributeButton;
    private JButton saveTemplateButton;
    private JFrame frame;
    private Optional<Path> selectedPath = Optional.empty();

    public AttributeEditorForm() {
        templateBox = new JComboBox<>();
        attributeTable = new JTable();
        templateEditor = new JPanel();
        newAttributeButton = new JButton();
        newAttributeButton.setText("Add");
        saveTemplateButton = new JButton();
        saveTemplateButton.setText("Save");

        templateEditor.add(templateBox);
        templateEditor.add(attributeTable);
        templateEditor.add(newAttributeButton);
        templateEditor.add(saveTemplateButton);

        templateBox.setRenderer(new TemplateRenderer());
        templateBox.addItemListener(e -> {
            if (e.getStateChange() == SELECTED) {
                TemplateAdaptor adaptor = (TemplateAdaptor) e.getItem();
                DefaultTableModel model = new DefaultTableModel();
                model.addColumn("Name");
                model.addColumn("Value");
                model.addColumn("Type");
                adaptor.getAttributes().forEach(a -> model.addRow(new Vector<>(asList(a.getName(),
                        a.getValue().toString(), strFunction().apply(a)))));
                attributeTable.setModel(model);
                addTypeEditor(attributeTable.getColumnModel().getColumn(2));
            }
        });
        newAttributeButton.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                DefaultTableModel model = ((DefaultTableModel) attributeTable.getModel());
                model.addRow(new Vector<>(asList("", "", "Int")));
            }
        });
        saveTemplateButton.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                DefaultTableModel model = ((DefaultTableModel) attributeTable.getModel());
                ArrayList<AttributeAdaptor> attributes = newArrayList();
                for (int i = 0; i < model.getRowCount(); i++) {
                    AttributeAdaptor attribute = byType((String) model.getValueAt(i, model.findColumn("Type")));
                    attribute.setName((String) model.getValueAt(i, model.findColumn("Name")));
                    attribute.valueFromString((String) model.getValueAt(i, model.findColumn("Value")));

                    attributes.add(attribute);
                }

                TemplateAdaptor changedTemplate = (TemplateAdaptor) templateBox.getSelectedItem();
                changedTemplate.setAttributes(attributes);


                List<TemplateAdaptor> adaptors = newArrayList();
                for (int i = 0; i < templateBox.getItemCount(); i++) {
                    adaptors.add(templateBox.getItemAt(i));
                }

                TemplateLoader.fileConsumer(selectedPath.get().toAbsolutePath().toString()).accept(adaptors.
                        toArray(new TemplateAdaptor[adaptors.size()]));
            }
        });
    }

    private AttributeAdaptor byType(String type) {
        switch (type) {
            case "Int" : return new NumberAttributeAdaptor();
            case "Flag": return new FlagAttributeAdaptor();
            case "String": return new StringAttributeAdaptor();
            case "Range": return new RangeAttributeAdapter();
            default: throw new RuntimeException(format("No type for %s", type));
        }
    }

    private Function<AttributeAdaptor, String> strFunction() {
        return (AttributeAdaptor x) -> {
            if (x instanceof FlagAttributeAdaptor) {
                return "Flag";
            } else if (x instanceof NumberAttributeAdaptor) {
                return "Int";
            } else if (x instanceof StringAttributeAdaptor) {
                return "String";
            } else if (x instanceof RangeAttributeAdapter) {
                return "Range";
            }
            throw new RuntimeException("Not expected to be here");
        };
    }

    private void addTypeEditor(TableColumn col) {
        JComboBox<String> innerCb = new JComboBox<>();
        innerCb.addItem("Flag");
        innerCb.addItem("Int");
        innerCb.addItem("String");
        innerCb.addItem("Range");
        col.setCellEditor(new DefaultCellEditor(innerCb));
    }

    public void initFrame() {
        JFrame frame = new JFrame("AttributeEditorForm");
        frame.setContentPane(templateEditor);
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.pack();
        this.frame = frame;
    }

    public void setFilePath(Path path) {
        selectedPath = Optional.of(path);
        List<TemplateAdaptor> templates = TemplateLoader.fileName(path.toAbsolutePath().toString()).get();
        templates.forEach(templateBox::addItem);
        templateBox.setSelectedIndex(0);
    }

    public JFrame getFrame() {
        return frame;
    }

    private static class TemplateRenderer implements ListCellRenderer<TemplateAdaptor> {

        @Override
        public Component getListCellRendererComponent(JList<? extends TemplateAdaptor> list, TemplateAdaptor value,
                                                      int index, boolean isSelected, boolean cellHasFocus) {
            JLabel label = new JLabel();
            label.setText(value.getName());
            return label;
        }
    }

}
